package rmiServeur;

import interfaces.Client_ITF;
import interfaces.Serveur_ITF;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Serveur_IMPL extends UnicastRemoteObject implements Serveur_ITF {

	/**
	 * @author arthur
	 * 
	 */
	public class FreeSemaphore implements Runnable {

		/**
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run() {
			semaphore = true;

		}

	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	HashMap<Integer, Client_ITF> clients;
	boolean semaphore;

	public Serveur_IMPL() throws RemoteException {
		super();
		clients = new HashMap<Integer, Client_ITF>();
		semaphore = true;
	}

	@Override
	public void addClient(Client_ITF c) throws RemoteException, ServerBusy {
		if (semaphore) {
			semaphore = false;
			clients.put(c.getId(), c);
			ScheduledExecutorService executor = Executors
					.newSingleThreadScheduledExecutor();
			executor.schedule(new FreeSemaphore(), 5, TimeUnit.SECONDS);
		} else {
			throw new ServerBusy();
		}
	}

	@Override
	public String getInfos() throws RemoteException {
		StringBuilder sb = new StringBuilder("Liste des clients\n");
		for (Client_ITF c : clients.values()) {
			sb.append(c.getId());
			sb.append(" \t");
			sb.append(c.getIp());
			sb.append("\n");
		}
		return sb.toString();
	}

	@Override
	public ArrayList<Client_ITF> getNeighbors(Client_ITF client)
			throws RemoteException {
		// Initialisation of the neighborsList
		ArrayList<Client_ITF> clientsList = new ArrayList<Client_ITF>();
		clientsList.addAll(clients.values());
		ArrayList<Client_ITF> result = new ArrayList<Client_ITF>();

		int clientIndex = clientsList.indexOf(client);

		// if there is one or two other clients registered we put all of them as
		// neighbors
		if (clientsList.size() > 1 && clientsList.size() < 4) {
			for (int i = 0; i < clientsList.size(); i++) {
				if (i != clientIndex) {
					result.add(clientsList.get(i));
				}
			}

		} else if (clientsList.size() > 3) {
			// if there is more than two other clients we choose randomly two of
			// them
			Random r = new Random();
			int newNeighborIndex;
			while (result.size() < 2) {
				newNeighborIndex = r.nextInt(clientsList.size());

				// We check that we doesn't choice randomly the requesting
				// client itself
				if (newNeighborIndex != clientIndex
						&& !result.contains(clientsList.get(newNeighborIndex))) {
					result.add(clientsList.get(newNeighborIndex));
				}
			}
		}

		// for each chosen client we add them a new neighbors (the requesting
		// client)
		for (Client_ITF newNeighbor : result) {
			try {
				newNeighbor.addNeighbor(client);
			} catch (RemoteException e) {
				System.err.println("L'ajout au client " + newNeighbor
						+ " le voisin " + client + " à échoué");
			}
		}

		return result;
	}

	@Override
	public void removeClient(Client_ITF client) throws RemoteException {
		clients.remove(client.getId());

		for (Client_ITF neighborOfOldClient : client.getNeighbors().values()) {
			neighborOfOldClient.removeNeighbor(client);
		}
	}

}
